home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Reference / DevCon / Orlando_1993 / Devcon93.4 / CAMD / docs / ecamd.readme < prev    next >
Encoding:
Text File  |  1993-02-01  |  36.8 KB  |  720 lines

  1. WARNING: This is an early specification for CAMD.  Keep in mind that 
  2. some of the information is out of date.  See the includes and Autodocs 
  3. for realtime.library and camd.library for the latest information.
  4.  
  5. CAMD: Commodore Amiga Multimedia Driver
  6.  
  7.     CAMD is a general library for synchronizing different multimedia
  8. applications and passing data at high rates from one application to
  9. another in real time. The emphasis is on MIDI and music applications;
  10. However, CAMD can be used to synchronize other types of applications
  11. as well, such as laser disk, animation players, sound effects, CD Audio,
  12. videotape / SMPTE and other real-time applications.
  13.  
  14. Issues being worked on:
  15.  
  16. Named Rendezvous:
  17.     The new architecture for CAMD will use the concept of named "rendezvous
  18. points". CAMD maintains a list of "names" which represent "meeting places"
  19. for real-time applications. (In the include files, a "meeting place" is
  20. referred to as a "cluster").
  21.     Each application can have any number of input or output linkages. Each
  22. MidiLink structure will have the name of a "meeting place" which will be used
  23. to rendezvous with other applications at the same "meeting place". Any number
  24. of senders and receivers can meet at a single meeting place. Once the
  25. particpants arrive, the ports are "wired" together with pointers so that
  26. communication can be very fast and efficient. Any participant can leave at
  27. any time; When all participants have left, the meeting place is
  28. deleted. Senders may send "events" which are clumps of data in MIDI format.
  29. (Each "clump" contains one complete MIDI message). If there are no
  30. receivers, the messages go nowhere, otherwise each receiver gets a copy of
  31. the sender's event. Events are buffered, using memory allocated by the
  32. receiving application, or by CAMD.
  33.     Some meeting places are "public" while others are "private". Public
  34. meeting places can be seen by the user -- for example, a MIDI user can see
  35. a scrolling list of meeting places, and select one for his application to
  36. rendezvous with. The user could also add a public meeting place to the list
  37. by clicking an "Add" button.
  38.     Private meeting places do not appear on user lists. They are used for
  39. special types of communication between applications that do not wish to be
  40. disturbed. Privacy is actually an attribute of a participant -- the meeting
  41. place is only private if all participants are private. This allows for
  42. participants to discretely open a port at a meeting place which will only
  43. become public if another participant joins in. This avoids cluttering up
  44. user lists with features that are not active.
  45.     The nice thing about the meeting place concept is that it is totally
  46. forgiving. If a user sets up his MIDI sequencer application to control a
  47. light-show application, using CAMD, he can save the configuration in the
  48. "Save Settings" menu. Then later, if he decides to run only one of those
  49. applications, it will still work, even though the other half of the link is
  50. not present. Thus, it will be easy for musicians and multimedia artists to
  51. create modular setups without having to spend tedious time linking and
  52. configuring their system because some software or equipment is not
  53. currently activated.
  54.     Meeting places can also have a comment field, which is determined
  55. by the comment field of the MidiLink with the highest priority that
  56. is attached to it.
  57.  
  58. Time structures for CAMD.
  59.  
  60.     Within the library base of CAMD is a central clock called the heartbeat.
  61. This clock is always counting, wrapping around when it overflows, at a rate
  62. of 500Hz. It is driven by one of the CIA timers (whichever one is available).
  63.     From the heartbeat clock are derived the various "Conductors". Each
  64. conductor represents a group of applications which wish to remain
  65. synchronized together. Like the meeting places, conductors are created
  66. automatically when one member attempts to open it, and are deleted when the
  67. last member leaves. There is a default conductor called "main" which is
  68. used as a default for applications who want to be synchronized with
  69. whomever else is out there.
  70.     Unlike meeting places, however, there is a limited number of 'senders',
  71. or in this case 'external time sources' such as SMPTE, MIDI Time Code, etc.
  72. There can be either zero or one external time sources per conductor. If
  73. there are zero, then it is a free-running clock, that is to say that it
  74. runs entirely from the heartbeat clock. If an external time source is
  75. present and active however, the conductor will still run off of the
  76. heartbeat clock, but the values will be constrained to be "near" the value
  77. of the external clock. Thus, the external clock keeps the internal clock in
  78. time, but is the internal clock that actually does the real work. Thus, we
  79. can "sing along" with SMPTE, but at a much higher clock rate and
  80. consequently much higher precision. After all, SMPTE only runs at 30 frames
  81. a second or so, while the heartbeat runs at 500 times a second.
  82.     Conductors have essentially three attributes: A list of clients, called
  83. "players" a current time value, and a "play state", which indicates if the
  84. Conductors is stopped, paused, locating or running. These states will be
  85. defined in more detail later.
  86.  
  87.     Attached to the Conductor are any number of players. A PlayerInfo
  88. is a structure created by the application, which is used to deliver clock
  89. pulses from the conductor to the application. This is done through
  90. two mechanisms: A "wake-up call", where the application can be informed
  91. whenever a particular point in time passes, and a "tick hook" which allows
  92. for an application callback function to be invoked each time a new clock
  93. value is posted.
  94.     In addition, there is a provision for applications to keep track of time
  95. in their own internal format. This is useful for music programs which may
  96. find measures and tempo (metered time) more useful than 500Hz ticks. In
  97. addition, CAMD allows for MIDI events posted to a particular port to be
  98. timestamped in the application's own format. Basically, each MidiNode
  99. contains a pointer to a longword value to use as the source of timestamping
  100. information. This could be used to point to any one of several fields
  101. in the PlayerInfo structure, depending on what type of timestamp was
  102. desired, or it could in fact point to any longword who's value is incremented
  103. on a regular basis.
  104.     The PlayerInfo field also contains a facility for signaling a task
  105. whenver a change of state occurs, such as play, stop, etc.
  106.  
  107.     There is also a facility for distributing metered time between players.
  108. The flag PLAYERF_CONDUCTED is set for applications that want to take advantage
  109. of this feature. The application who's PlayerInfo node has the highest
  110. priority will determine the time for applications of lower priority.
  111. The first CONDUCTED player fills in the cdt_Metronome with the number
  112. of 384ths of a quarter note (unless someone has a beter number they want
  113. to use...) that have elapsed.
  114.     Note that this only allows one "kind" of metered time to be distributed
  115. between players, namely musical time. Note that applications can still
  116. distribute MIDI Clocks to one another, if they need something special.
  117.  
  118. A word of note on System overhead:
  119.     A number of program authors are concerned because of the fact that some
  120. of the CIA timers run on a higher interrupt level than the Amiga serial
  121. port -- thus, potentially interfering with MIDI reception. Rest assured
  122. that CAMD will do most of it's work in a Soft Interrupt (the lowest level
  123. interrupt) and that the timer interrupt is extremely short.
  124.  
  125. Clock States:
  126.     The four states are: STOP, PAUSE, LOCATE and PLAY. The can be set by
  127. anybody at any time (even an interrupt). Thus, if one application signals
  128. to start playing, they all play together. Other applications which are
  129. driven from different Conductor are not affected, of course.
  130.     An external timing source can also start the clock. This allows the
  131. user to start the playback simply by pressing the "play" button on the
  132. videotape recorder, laser disk, etc.
  133.     There are two ways to start the clock: The first way is to set the
  134. clock to CLOCKSTATE_RUNNING. This starts the clock immediately, and then
  135. informs the applications that the clock has started, and what time it
  136. started at. This will most often be the case for an external time source,
  137. such as SMPTE -- it can't slow down the tape to wait for the applications
  138. to get ready.
  139.     The other way is to set the state to CLOCKSTATE_LOCATE. This informs each
  140. player that the clock is about to start (and what time), then waits until
  141. each application has set its 'ready' bit. At that point, the state changes
  142. to CLOCKSTATE_RUNNING. The purpose of this is simple: Some applications can't
  143. start immediately, because of the fact that they have to set their current
  144. "state" based on the starting time. In the case of some music applications,
  145. this can take considerable time, as the application attempts to determine
  146. the "state" of all of it's internal variables for a particular clock time.
  147. So the CLOCKSTATE_LOCATE state allows simpler applications to wait until the
  148. more complex ones get ready.
  149.     You may be asking: What happens when an external timing source sets the
  150. clock to CLOCKSTATE_RUNNING? What happens to these applications that can't
  151. start immediately? The answer is that they now have to "chase" the current
  152. clock time, like a man running after a train that is leaving the station,
  153. until they "catch up" at which point they will join in along with the
  154. others. This turns out to be no problem in practice, and professional
  155. sequencers do it all the time.
  156.  
  157. More on External Synchronization Sources:
  158.     An external timing source is a special kind of player. It sets a
  159. flag in it's PlayerInfo structure indicating that new clock times should
  160. never be posted. (In fact the only purpose of the PlayerInfo in this case
  161. is to keep the Conductor from being deleted in the case that all other
  162. PlayerInfos should go away).
  163.     The software driver for the external input can, at any time, call the
  164. CAMD function ExternalSync(). This function constrains the Conductor to
  165. only allow clock values which are between the time of the current external
  166. clock tick and the estimated time of the next external clock tick. Each time
  167. an external timing pulse is received, it should call this function to allow
  168. the conductor to tick ahead a few more units. If the conductor falls
  169. behind, ExternalSync() will push it ahead to catch up with the external
  170. time, and if it pulls to far ahead, ExternalSync will cause it to stop and
  171. wait while the external clock catches up.
  172.     I envision that external sync drivers will come as 3rd-party utilities
  173. with their own user interface. The utility will open a window which allows
  174. the user to set the following attributes:
  175.  
  176.     -- The name of the conductor to affect.
  177.     -- A checkbox to enable or disable AutoStart
  178.     -- A reading of the current external time value in whatever units
  179.         appropriate, such as SMPTE time code
  180.     -- an offset value, used to 'bias' the time (change the origin of zero)
  181.     -- Various device-specific parameters.
  182.  
  183.     The "AutoStart" feature simply means that the external timing driver
  184. should start the clock when the external source starts, and should stop it
  185. when the external source stops.
  186.     Note that when the external source is not running, the software driver
  187. can "release" the Conductor so that it becomes a free-running clock
  188. again. This allows users to play their melodies, test them out and edit
  189. them, while the tape is not running -- without having to press any special
  190. buttons.
  191.     (Issue: If that's the case, does 'Autostart' have any meaning? Instead
  192. should we have a 'disable' button that allows them to play the tape without
  193. playing the music?)
  194.     (Issue: We should also have a mode where the clock does NOT become
  195. a free-running clock when the tape is not running. This will allow us
  196. to have applications pre-located and "waiting" for the external clock
  197. to start).
  198.  
  199. Private Conductors
  200.     Sometimes, an application may which to have a free-running source-clock
  201. of it's own, so that it can get timing-based services unrelated to the
  202. externally-synchronized tracks. For example, dragging notes with the mouse
  203. may require a timing signal to shut off the note after so many seconds, and
  204. you would not want this to be controlled by the video tape -- in fact,
  205. you would want this to work even if the clock were stopped. How the
  206. application manages the complexities of dealing with more than one
  207. PlayerInfo is left as an excersize to the reader.
  208.  
  209. Details of the Conductor
  210.  
  211.     Note that all time values can be negative. This is useful in cases
  212. where either the videotape has rewound back too far, or where the
  213. application wishes to provide a "count-in".
  214.  
  215.     cdt_ClockTime: This is the current value of the clock. It is
  216. incremented once each heartbeat, subject to external constraints. It will
  217. never go backwards while the clock is running -- if it does, then the clock
  218. is considered to have "stopped" and "rewound".
  219.     Note: If the clock is being driven externally, ClockTime will not
  220. be incremented until the first external "tick" is received. This is to
  221. prevent the music from playing one tick's worth while waiting for the
  222. tape to start.
  223.  
  224.     cdt_StartTime: This is the starting time of the clock. It is set when
  225. the clock goes into either LOCATE or RUNNING state.
  226.  
  227.     cdt_ExternalTime: The clock time of the last external timing pulse.
  228. If external sync is enabled, the clock will never be earlier than this.
  229. Note that the external software driver will have to convert from it's units
  230. to 500Hz CAMD units.
  231.  
  232.     cdt_MaxExternalTime: The estimated clock time of the next external
  233. timing pulse. If the ClockTime is greater than this, it will not be
  234. incremented.
  235.     (Note: Signed comparisons are used, in a "wrap-around" manner)
  236.  
  237.     cdt_Metronome: This is the metered time to be ditributed to applications
  238. that want it. The CONF_METROSET flag will be set if this field has been filled
  239. in. If it is not set, and the application is expecting metered time, then
  240. the application's time hook must fill it in itself, so that other players will
  241. be able to follow it's lead.
  242.  
  243.     cdt_PlayersNotReady: This is a count of the number of PlayerInfos
  244. that are not ready to go into RUNNING mode.
  245.  
  246.     cdt_State: This is the current playing state.
  247.     
  248.     cdt_Players: This is an Exec List of PlayerInfos.
  249.  
  250. struct Conductor {
  251.     struct Node       cdt_Node;        /* conductor list node        */
  252.     struct MinList cdt_Players;        /* list of players        */
  253.     ULONG       cdt_Flags;        /* clock flags            */
  254.     ULONG       cdt_ClockTime,    /* current time of this sequence*/
  255.            cdt_StartTime,    /* start time of this sequence    */
  256.            cdt_ExternalTime,    /* time from external unit    */
  257.            cdt_MaxExternalTime,    /* upper limit on sync'd time    */
  258.            cdt_Metronome;     /* metric time of highest pri node */
  259.     UWORD       cdt_PlayersNotReady;    /* count of players not ready    */
  260.     UBYTE       cdt_State;        /* playing or stopped        */
  261.     UBYTE          cdt_Pad[1];
  262. };
  263.  
  264. #define CONDUCTF_EXTERNAL    (1<<0)       /* clock is externally driven   */
  265. #define CONDUCTF_GOTTICK     (1<<1)       /* received 1st external tick   */
  266. #define CONDUCTF_METROSET    (1<<2)       /* metered tie filled in        */
  267.  
  268. enum time_states {
  269.     CLOCKSTATE_STOPPED=0,                 /* clock is stopped             */
  270.     CLOCKSTATE_PAUSED,                    /* clock is paused              */
  271.     CLOCKSTATE_LOCATE,                    /* go to 'running' when ready   */
  272.     CLOCKSTATE_RUNNING,                   /* run clock NOW                */
  273. };
  274.  
  275. Details of Players:
  276.  
  277.     pi_Hook: This is a pointer to the application's Callback function. The
  278. callback function is always called in the context of a software interrupt.
  279.     (ISSUE: List calling parameters/registers for hooks)
  280.     Also, the Hook structure conveniently contains a Node structure which
  281. is used to maintain a list of PlayerInfos.
  282.  
  283.     pi_Source: A pointer to the Conductor driving this clock.
  284.     
  285.     pi_Task: The task to signal when a state change occurrs.
  286.     (ISSUE: What about non-task-based sequencers?)
  287.     
  288.     pi_StateSignal: The signal to use when signaling the task that the state
  289.         has changed.
  290.  
  291.     pi_AlarmSignal: The signal to use when signaling the task that the alarm
  292.         has gone off.
  293.     
  294.     pi_MetricTime: The current time, converted to the application's own
  295. metric. The flag PLAYERF_METRIC indicates whether this field is
  296. filled in by CAMD (to the conductor's current time) or filled in
  297. by the application (either by the tick hook or by the application's
  298. task).
  299.  
  300.     pi_AlarmTime: The clock time of when to call the hook function next.
  301.     
  302.     pi_Flags:
  303.         PLAYERF_READY: Indicates that the player is ready to run.
  304.         PLAYERF_WAIT: Indicates that the player is waiting for an alarm.
  305.         PLAYERF_CONTINUOUS: Call the TickHook every cycle.
  306.         PLAYERF_METRIC: Indicates that the tick hook will fill in the
  307.             pi_MetricTime
  308.         PLAYERF_NULL: This player is inactive, ignore it.
  309.         PLAYERF_ALARMSIG: Use the signal to alarm the task, not the callback.
  310.         PLAYERF_CONDUCTED: This application wants to receive metered time.
  311.             The player must have a callback hook in CONTINUOUS mode.
  312.  
  313.     pi_UserData: For application use.
  314.     pi_PlayerID: For application use.
  315.  
  316. struct PlayerInfo {
  317.     struct Node       pi_Node;              /* player node                 */
  318.     struct Hook       pi_Hook;             /* callback for player          */
  319.     struct Conductor *pi_Source;           /* pointer to parent context    */
  320.     struct Task      *pi_Task;             /* task to signal for changes   */
  321.     ULONG             pi_StateSignal,      /* signal to send for changes   */
  322.                       pi_AlarmSignal;      /* signal to send for alarms    */
  323.     LONG              pi_MetricTime;       /* current time in app's metric */
  324.     LONG              pi_AlarmTime,        /* time to wake up              */
  325.     ULONG             pi_Flags;            /* general PlayerInfo flags     */
  326.     void             *pi_UserData;         /* for application use          */
  327.     LONG              pi_PlayerID;         /* for application use          */
  328. };
  329.  
  330. #define PLAYERF_READY      (1<<0)     /* player is ready to go!           */
  331. #define PLAYERF_WAIT       (1<<1)     /* call hook when alarm goes off    */
  332. #define PLAYERF_CONTINUOUS (1<<2)     /* call hook every tick             */
  333. #define PLAYERF_METRIC     (1<<3)     /* hook should fill in MetricTime   */
  334. #define PLAYERF_NULL       (1<<4)     /* a dummy player, used for    sync */
  335. #define PLAYERF_ALARMSIG   (1<<5)     /* use signal for alarm, not callback */
  336. #define PLAYERF_CONDUCTED  (1<<6)     /* give me metered time              */
  337.  
  338. Using the callback hook:
  339.     If the CONDUCTED flag is set
  340.         if the conductor's METROSET is NOT set:
  341.             convert cdt_ClockTime to cdt_Metronome
  342.             set METROSET
  343.         convert cdt_Metronome to pi_MetricTime
  344.     else if the PLAYERF_METRIC flag is set
  345.         convert cdt_ClockTime to pi_MetricTime
  346.     else ;
  347.  
  348.     if (MetricTime > (some time, say AlarmTime)) then wake up application...
  349.  
  350. A note on MIDI Clocks:
  351.  
  352.     Since CAMD is a MIDI driver, it should be able to respond either to
  353. MIDI clocks or MIDI time code. Actually, it's probably not possible to
  354. interpret MIDI clocks, since they are really dependent on the application's
  355. interpretation of tempo. Instead, these should probably just be passed on
  356. to the application directly. We _might_ be able to help out by recording the
  357. time interval between MIDI clocks so that the application can interpolate
  358. the tempo.
  359.     MIDI Time Code, on the other hand, is a lot like SMPTE. Basically,
  360. CAMD should be able to convert a MIDI Time Code event into a frame number
  361. internally, and drive the correct clock with it. It will need a library
  362. interface to set up the variables (autostart, etc.), although we _could_
  363. pass those things on to the application and let them handle it.
  364.     In addition, the App should have the option of processing the MTC
  365. itself, in case they decide that they aren't using our clock.
  366.  
  367.     MIDI Start and Stop should be handled by the application, probably. It
  368. can decide if it wants to start the clock or not.
  369.  
  370. Application-Specific Time:
  371.     It is the job of the application to convert CAMD's clock times into
  372. it's own 'Metric', or local format, but CAMD provides some help in
  373. this regard.
  374.     This section doesn't describe any specific features of CAMD, but
  375. rather contains some advice on how to get tempos that are accurate over
  376. long periods of time (important for film and video work).
  377.     I have found that the most accurate way to convert time from one
  378. format to another is to not deal with the time values directly, but
  379. only with their delta-values. Basically, when a new clock time arrives,
  380. subtract it from the previous clock time to get the delta time.
  381. Then do the neccessary multiplication and division to get the converted
  382. delta time. Be careful to save any leftover remainders, and accumlate them
  383. into the total.
  384.  
  385.     Example:
  386.  
  387.         input_delta = clock_time - last_time;
  388.         last_time = clock_time;
  389.         
  390.         output_delta = (input_delta * k1 + last_rmainder) / k2;
  391.         last_remainder = (input_delta * k1 + last_remainder) % k2;
  392.             /* (actually you would just do this in assembly and take
  393.                 the remainder of the divide).
  394.             */
  395.  
  396.         local_time += output_delta;
  397.  
  398.     This method allows for expremely accurate timing over the long term.
  399. With this formula, assuming that tempos are stored in 32-bit fixed-point
  400. precision (16 bit integer, 16 bit fraction), you could have a song 23
  401. minutes long, and adjusting the tempo up or down by the smallest possible
  402. increment would allow you to change the timing of the final beat of the
  403. song by one film frame! (That's if I have my calculations right, of
  404. course). I think that this will satisfy the film-scoring people well enough.
  405.  
  406.     Note that the tempo conversion routines should probably _not_ be called
  407. when locating, as the locator will want to adjust the pi_MetricTime in
  408. leaps and jumps as it locates through the song.
  409.  
  410.     In addition, the TickHook should also do what's needed for actually
  411. playing back the notes. In my cases, I send a signal to a high-priority
  412. task, telling it to wake up and play the next batch of notes, if needed
  413. (actually, the UserData points to a structure which tells it at what metric
  414. time it should wake up the task... as a result, the task is not woken up
  415. uneccesarily).
  416.  
  417. =================== Library Function Protypes ===========================
  418.  
  419.     /* ---- list access */
  420. void LockCAMD(ULONG);
  421. void UnlockCAMD(ULONG);
  422.  
  423.     /* ---- MidiNode */
  424. struct MidiNode *CreateMidiA(STRPTR, struct TagItem *);
  425. struct MidiNode *CreateMidi(STRPTR, Tag tag, ...);
  426. void DeleteMidi (struct MidiNode *);
  427. BOOL SetMidiAttrsA(struct MidiNode *, struct TagItem *);
  428. BOOL SetMidiAttrs(struct MidiNode *, Tag tag, ...);
  429. BOOL GetMidiAttr(struct MidiNode *, LONG, void *, LONG);
  430. struct MidiNode *NextMidi(struct MidiNode *);
  431. struct MidiNode *FindMidi(STRPTR);
  432. void FlushMidi (struct MidiNode *);
  433.  
  434.     /* ---- MidiLink */
  435. struct MidiLink *AddMidiLinkA(struct MidiNode *, STRPTR, LONG, 
  436.                                               struct TagItem *);
  437. struct MidiLink *AddMidiLink(struct MidiNode *, STRPTR, LONG, Tag tag, ...);
  438. BOOL SetMidiLinkAttrsA(struct MidiLink *, struct TagItem *);
  439. BOOL SetMidiLinkAttrs(struct MidiLink *, Tag tag, ...);
  440. BOOL GetMidiLinkAttr(struct MidiLink *, LONG, void *, LONG);
  441. void RemoveMidiLink(struct MidiLink *);
  442. struct MidiLink *NextClusterLink(struct MidiCluster *, struct MidiLink *, BOOL);
  443. struct MidiLink *NextNodeLink(struct MidiNode *, struct MidiLink *, BOOL);
  444. BOOL MidiLinkConnected(struct MidiLink *);
  445.  
  446.     /* ---- MidiCluster */
  447. struct MidiCluster *NextCluster(struct MidiCluster *);
  448. struct MidiCluster *FindCluster(STRPTR);
  449.  
  450.     /* ---- Message */
  451. void PutMidi (struct MidiLink *, LONG);
  452. void PutSysEx (struct MidiLink *, UBYTE *);
  453. BOOL GetMidi (struct MidiNode *, MidiMsg *);
  454. BOOL WaitMidi (struct MidiNode *, MidiMsg *);
  455. ULONG GetSysEx (struct MidiNode *, UBYTE *, ULONG);
  456. ULONG QuerySysEx (struct MidiNode *);
  457. void SkipSysEx (struct MidiNode *);
  458. UBYTE GetMidiErr (struct MidiNode *);
  459. WORD MidiMsgType (MidiMsg *);
  460. WORD MidiMsgLen (ULONG StatusByte);
  461.  
  462.     /* ---- Conductors */
  463. struct PlayerInfo *CreatePlayerA(STRPTR,STRPTR,struct TagItem *);
  464. struct PlayerInfo *CreatePlayer(STRPTR,STRPTR,Tag tag,...);
  465. void DeletePlayer(struct PlayerInfo *);
  466. BOOL SetPlayerAttrsA(struct Player *, struct TagItem *);
  467. BOOL SetPlayerAttrs(struct Player *,Tag tag,...);
  468. BOOL SetConductorState(struct Player *, LONG, LONG);
  469. BOOL ExternalSync(struct PlayerInfo *, LONG, LONG);
  470.  
  471.     /* ---- Devices */
  472. struct MidiDeviceData *OpenMidiDevice (UBYTE *Name);
  473. void CloseMidiDevice (struct MidiDeviceData *);
  474.  
  475. =================== Some Important Structures ===========================
  476.  
  477. /***************************************************************
  478. *
  479. *   MidiLink -- links a cluster and a MidiNode
  480. *
  481. *   All fields are READ ONLY.  Modifications to fields may
  482. *   performed only through the appropriate library function
  483. *   calls.
  484. *
  485. ***************************************************************/
  486.  
  487. struct MidiLink {
  488.     struct Node          ml_Node;            /* node for cluster list */
  489.     WORD                 ml_Pad;
  490.     struct MinNode       ml_OwnerNode;       /* node for interface list */
  491.     struct MidiNode     *ml_MidiNode;        /* interface we belong to... */
  492.     struct MidiCluster  *ml_Location;        /* location we are a member of */
  493.     char                *ml_ClusterComment;  /* comment for cluster    */
  494.     UBYTE                ml_Flags;           /* general flags          */
  495.     UBYTE                ml_PortID;          /* number of this port    */
  496.     UWORD                ml_ChannelMask;     /* mask flags for channel */
  497.     ULONG                ml_EventTypeMask;   /* mask flags for events  */
  498.  
  499.     /* Sys/Ex Filter */
  500.     union SysExFilter 
  501.     {
  502.     UBYTE            b[4];                    /* 1 byte mode, 3 bytes for match id(s) */
  503.     ULONG            sxf_Packed;
  504.     } ml_SysExFilter;
  505.  
  506.     APTR            ml_UserData;            /* attached to events... */
  507. };
  508.  
  509.     /* Types of links (used for creation) */
  510.  
  511. enum {
  512.     MLTYPE_RECEIVER=0,
  513.     MLTYPE_SENDER
  514. };
  515.  
  516. #define MLF_SENDER         (1<<0)                /* this link sends from app */
  517. #define MLF_PARTCHANGE     (1<<1)                /* part change pending */
  518. #define MLF_PRIVATELINK    (1<<2)                /* make this link private */
  519.  
  520. #define MLINK_Base    TAG_USER+64
  521.  
  522. #define MLINK_Location     (MLINK_Base+1)   /* rendezvous point     */
  523. #define MLINK_ChannelMask  (MLINK_Base+2)   /* channel mask bits    */
  524. #define MLINK_EventMask    (MLINK_Base+3)   /* event type mask bits */
  525. #define MLINK_UserData     (MLINK_Base+4)   /* user data for incoming events */
  526. #define MLINK_Comment      (MLINK_Base+5)   /* comment for cluster           */
  527. #define MLINK_PortID       (MLINK_Base+6)   /* port id number for MidiMsg    */
  528. #define MLINK_Private      (MLINK_Base+7)   /* not a public link    */
  529. #define MLINK_Priority     (MLINK_Base+8)   /* priority of node     */
  530. #define MLINK_SysExFilter  (MLINK_Base+9)   /* three 1-byte headers */
  531. #define MLINK_SysExFilterX (MLINK_Base+10)  /* one 3-byte header    */
  532.  
  533. /***************************************************************
  534. *
  535. *   MidiNode
  536. *
  537. *   All fields are READ ONLY.  Modifications to fields may
  538. *   performed only through the appropriate library function
  539. *   calls.
  540. *
  541. ***************************************************************/
  542.  
  543. struct MidiNode 
  544. {
  545.     struct Node      mi_Node;             /* linked list node      */
  546.     UWORD            mi_ClientType;       /* type of application   */
  547.     struct Image    *mi_Image;            /* image for patch panel */
  548.  
  549.     struct MinList   mi_OutLinks,         /* list of output links  */
  550.                      mi_InLinks;          /* list of input links   */
  551.     
  552.     struct Task     *mi_SigTask;          /* task to signal        */
  553.     struct Hook     *mi_ReceiveHook,      /* hook (and list node)  */
  554.                     *mi_ParticipantHook;  /* hook for participant change    */
  555.     BYTE             mi_ReceiveSigBit,    /* signalmask for new data        */
  556.                      mi_ParticipantSigBit;/* signalmask for part change     */
  557.     UBYTE            mi_ErrFilter;        /* CMEF_ error filter for ErrFlags*/
  558.     UBYTE            mi_Alignment[1];     /* for longword alignment         */
  559.  
  560.     ULONG           *mi_TimeStamp;        /* where timestamps come from     */
  561.  
  562.     ULONG            mi_MsgQueueSize,     /* size of buffers       */
  563.                      mi_SysExQueueSize;
  564.  
  565.     /* private stuff below here */
  566. };
  567.  
  568.     /* client types */
  569.  
  570. #define CCType_Sequencer          (1<<0)
  571. #define CCType_SampleEditor       (1<<1)
  572. #define CCType_PatchEditor        (1<<2)
  573. #define CCType_Notator            (1<<3)        /* transcription of MIDI notes    */
  574. #define CCType_EventProcessor     (1<<4)        /* any data altering functions    */
  575. #define CCType_EventFilter        (1<<5)
  576. #define CCType_EventRouter        (1<<6)        /* e.g MIDI thru task */
  577. #define CCType_ToneGenerator      (1<<7)        /* i.e.using Amiga to make sound*/
  578. #define CCType_EventGenerator     (1<<8)        /* e.g algorithmic composition */
  579. #define CCType_GraphicAnimator    (1<<9)        /* e.g syncing animation software to MIDI */
  580.  
  581.  
  582.     /* Tags for CreateMidi() and SetMidiAttrs() */
  583.  
  584. #define MIDI_Base           (TAG_USER+64)
  585. #define MIDI_Name           (MIDI_Base+1)    /* name of application */
  586. #define MIDI_SignalTask     (MIDI_Base+2)    /* task to signal if not this one*/
  587. #define MIDI_RecvHook       (MIDI_Base+3)    /* hook for incoming data */
  588. #define MIDI_PartHook       (MIDI_Base+4)    /* hook for participant change */
  589. #define MIDI_RecvSignal     (MIDI_Base+5)    /* signal to use if incoming */
  590. #define MIDI_PartSignal     (MIDI_Base+6)    /* signal to use if incoming */
  591. #define MIDI_MsgQueue       (MIDI_Base+7)    /* size of event buffer */
  592. #define MIDI_SysExSize      (MIDI_Base+8)    /* size of sysex buffer */
  593. #define MIDI_TimeStamp      (MIDI_Base+9)    /* timer to timestamp with */
  594. #define MIDI_ErrFilter      (MIDI_Base+10)   /* error filter bits */
  595. #define MIDI_ClientType     (MIDI_Base+11)   /* client type mask */
  596. #define MIDI_Image          (MIDI_Base+12)   /* application image */
  597.  
  598.     /* SysExFilter members */
  599. #define sxf_Mode b[0]        /* SXFM_ mode bits */
  600. #define sxf_ID1  b[1]        /* 3 1-byte id's or 1 3-byte id */
  601. #define sxf_ID2  b[2]
  602. #define sxf_ID3  b[3]
  603.  
  604. =========================== Application Notes ============================
  605.  
  606. Locating:
  607.  
  608.     On some applications, locating is very simple: Just take the current
  609. conductor time, convert to to a local MetricTime, and poke it into the
  610. correct variable.
  611.     However, for some MIDI applications, life is not so simple. Because the
  612. playback of a sequence can cause many different state changes, both to
  613. external MIDI equipment and the sequencer's internal state, it is not easy
  614. to determine the total "state" of the sequencer for any particular time
  615. without actually playing the sequence. So, many sequencers do just that --
  616. they "play" the sequence (silently, with notes supressed) to themselves as
  617. fast as they can, until the silent playback reaches the point at which the
  618. current clock setting. At that point, they enable note playing and follow
  619. along with the other participants at normal speed.
  620.     In a multi-track sequencer, this can be a complex process. One
  621. technique is, each time a track event is processed, the track with the next
  622. lowest non-processed event is processed. If the tracks are in the
  623. application's local metric (which they probably are) this can be even more
  624. complex. Either way, the "position time" is set to the time of the last
  625. processed event. This represents the clock time at which the sequencer is
  626. really positioned to at that instant.
  627.     A potential problem lies in the fact that the time to do this is
  628. non-zero. Two things can happen: 1. The clock starts running before the
  629. sequencer is ready (before it has reached the start point), in which case
  630. the sequencer will have even further to go before it can start playing for
  631. real. In this case, the first couple of notes from that sequencer will be
  632. absent, since it was busy catching up and couldn't play them.
  633.     The other situation is when the sequencer is busy trying to locate, and
  634. some other agency changes the clock setting out from under it. This is
  635. especially serious if reading from videotape and we have batches of
  636. non-continuous timecode (In other words, they haven't re-striped the tape
  637. after assembling the individual scenes). (Note that a "smart" time code
  638. reader could have translation tables to overcome this problem, but that's
  639. another product entirely).
  640.     If the new clock setting is the same as the old one, then there's no
  641. problem. If the new clock setting is _later_ than the old one, then there's
  642. also no problem -- the sequencer can continue locating, but this time it
  643. just goes on past the old location and proceeds to the new one. If the new
  644. location is _slightly_ earlier than the old one, then it's a toss-up -- the
  645. sequencer could, if it wanted, simply declare "close enough" and flag
  646. itself as 'ready'. When the clock actually starts running, the sequencer
  647. will be waiting around a bit while the others catch up, since it got
  648. located too far ahead. When the others catch up, they will all be in sync.
  649. Note that this technique should _not_ be used when starting at the
  650. beginning of the song, since we want everybody together on the first note.
  651. If starting from the middle of a song, it's not so serious.
  652.     Finally, if the new location is significantly earlier than the old
  653. location, the sequencer will have no choice but to start the location
  654. process all over again. Tough luck. (Fortunately, this case will be detected
  655. instantly, because the equation which terminates the location process
  656. (positiontime >= starttime) will suddenly and unexpectedly be true).
  657.     What that means is simple: If, during a locate, someone changes the
  658. time, a new LOCATE or RUNNING signal will be sent to all applications...
  659. but if your application is still in the locating process, you can pretty
  660. much ignore it. (Use SetSignal to check and reset the signal...)
  661.  
  662.     Another problem arises in that for many music-applications, the
  663. relationship between the CAMD time and the applications local time format
  664. is not always linear. An example of this is tempo change, either "instant"
  665. tempo changes, or gradual tempo changes (accelerando and ritardando).
  666.     Essentially, the relationship between musical time and real time
  667. is mathematically equivalent to taking the value of tempo (changing or
  668. not) and performing an integration on it, with respect to time. Doing
  669. this in real-time isn't always easy. It's especially difficult because
  670. the kinds of tempo changes the human ear likes to hear best are (e^x)
  671. curves -- the same curve you get when you have compound interest that
  672. is compounded continuously.
  673.     Another way of looking at it is that the tempo should change linearly
  674. wth respect to METERED time, but not linearly with respect to REAL time.
  675. (Since the tempo is changing, the relationship between real and metered
  676. is not linear).
  677.     Doing this during playback is actually pretty easy -- each time
  678. you get a new clock tick in the local meter, update the tempo by some
  679. fixed amount. It's during a locate where the situation gets tough.
  680.     This is because there are lots of cases during the locate process where
  681. you will want to know the Conductor time for a given Metered Time
  682. (the reverse of the usual conversion). For example, if your next event
  683. in the queue is at some particular metered time, you want to know how far
  684. forward to wind the conductor to get there.
  685.     Unfortunately, the simple summation technique described above only
  686. works in one direction. You can't use it to convert the other way...even
  687. in the simplest case (flat tempo) the numbers aren't going to come out
  688. exactly the same, which means that you are going to locate to just slightly
  689. the wrong place and be just slightly out of sync.
  690.     The best advice I can give at this point is to study your logarithms.
  691. You can get good tempo changes by interpolating over a table of logs, and
  692. you can get very accurate logarithms in only a few machine instructions,
  693. if you take logs to the base 2.
  694.  
  695. User Interface issues:
  696.     What should the interface be for getting applications together on
  697. a Conductor group? What determines whether an application is independent
  698. or joins with a sync group?
  699. =========================================================================
  700. Issues that haven't been completely addressed: (Note that the old
  701. CAMD didn't address these issues either).
  702.  
  703.     Several people seem to want the ability to have a setup involving a
  704. filter type module, where if the filter is not present, it goes right
  705. "through" and connects directly to what the filter would have connected to.
  706.     This becomes more complex when we think about clusters, because what
  707. the filter was connected "to" may be less well defined. Perhaps it's
  708. better to say "If this application is talking to nobody, then here's
  709. another place where we might find some listeners..."
  710.     Another way to handle it might be to replace the filter with a
  711. "thru"-style connection, which gets supplanted by the application.
  712.     So then what we need is 1) A utility that will establish "thru" links.
  713. 2) A way of making thru links be supressed whenever a certain other link
  714. is established.
  715.  
  716.     Which brings us to the second issue: Thru processing -- the ability to
  717. set up a direct copying process between two clusters. This can be done
  718. with a "callback" style of receiver, which just echoes each posting to the
  719. sender.
  720.